/*
 *
 * Copyright (C) 2012 Walter Lütgenau
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */
package offset.nodes.server.search.view;

import java.util.List;
import javax.faces.context.FacesContext;
import javax.jcr.RepositoryException;
import javax.jcr.query.QueryResult;
import javax.jcr.query.Row;
import javax.jcr.query.RowIterator;
import javax.servlet.http.HttpServletRequest;
import offset.nodes.server.data.view.NodesWrapper;
import offset.nodes.server.view.list.AbstractListManager;
import offset.nodes.server.view.list.ColumnInfo;
import offset.nodes.server.view.list.DefaultColumnManager;
import offset.nodes.server.view.list.IteratorList;
import offset.nodes.server.view.list.ListManager;
import offset.nodes.server.view.list.ListPager;
import offset.nodes.server.view.list.ObjectWrapper;
import offset.nodes.server.view.list.PageableList;
import offset.nodes.server.view.list.Pager;
import offset.nodes.server.view.list.SortOrderChangeListener;
import offset.nodes.server.view.list.Sorter;
import offset.nodes.server.view.list.SortingInfo;
import offset.nodes.server.view.list.SqlSorter;
import offset.nodes.server.view.list.TextSorter;
import offset.nodes.server.view.list.XPathParser;
import offset.nodes.server.view.list.XPathSorter;
import offset.nodes.server.model.RepositoryUtils;
import offset.nodes.server.search.view.NodesToMapWrapper;
import offset.nodes.server.search.view.RowToMapWrapper;
import offset.nodes.server.search.model.SearchCache;
import offset.nodes.server.search.model.SearchEntry;
import offset.nodes.server.search.model.SearchModel;
import offset.nodes.server.search.model.SearchType;
import offset.nodes.server.view.FacesUtils;

/**
 *
 * @author Walter Lütgenau
 */
public class QueryListManager extends AbstractListManager implements ListManager, SortOrderChangeListener {
    String key;
    ColumnInfo columnInfo = null;
    SortingInfo sortingInfo = null;
    String query = null;
    SearchType type = null;
    List result = null;

    public QueryListManager(String key) {
        this.key = key;
        this.type = SearchType.xpath;
    }

    public QueryListManager() {
        
    }
    
    class RowsWrapper implements ObjectWrapper {

        public int getTotalSize(Object o) {
            return (int) ((RowIterator)o).getSize();
        }

        public boolean isWrappedObject(Object o) {
            return (o instanceof Row);
        }

        public boolean isWrappedObjectContainer(Object o) {
            return (o instanceof RowIterator);
        }

        public Object wrapObject(Object o) {
            return new RowToMapWrapper((Row)o);
        }
        
    }

    protected void init() {
        if (query == null && key != null) {
            SearchEntry searchEntry = SearchCache.getInstance(FacesUtils.getRequest()).getSearch(key);
            query = searchEntry.getValue();
            type = searchEntry.getType();
        }
    }

    protected void runQuery() throws RepositoryException {
        init();
        
        QueryResult result = null;
        if (sortingInfo != null) {
            query = query + " " + createSorter(getSortingInfo(), getQueryType()).getSortString();
        }

        boolean rows = true;
        ObjectWrapper wrapper;
        String[] columnSpecifier = null;
        switch (type) {
            case xpath:
                result = getModel().executeXPathQuery(query);
                XPathParser parser = new XPathParser(query);
                columnSpecifier = parser.getColumnSpecifier();
                wrapper = new NodesToMapWrapper(getModel().getSession());
                rows = false;
                break;

            case sql:
                result = getModel().executeSql1Query(query);
                rows = true;
                wrapper = new RowsWrapper();

            default:
                result = getModel().executeTextQuery(query);
                rows = false;
                wrapper = new NodesWrapper();
        }

        // update persistent information
        if ( rows )
            setResult(new IteratorList(result.getRows(), wrapper));
        else
            setResult(new IteratorList(result.getNodes(), wrapper));

        if ( columnSpecifier != null)
            setColumnInfo(new DefaultColumnManager(result.getColumnNames(), columnSpecifier));
        else
            setColumnInfo(new DefaultColumnManager(result.getColumnNames()));

        if (sortingInfo == null) {
            sortingInfo = new SortingInfo();
            sortingInfo.addSortOrderChangeListener(this);
        }
    }

    protected Sorter createSorter(SortingInfo sortingInfo, SearchType queryType) {
        switch (queryType) {
            case xpath:
                return new XPathSorter(sortingInfo);

            case sql:
                return new SqlSorter(sortingInfo);

            default:
                return new TextSorter();
        }
    }

    public Pager getPager() {
        return new ListPager((PageableList) getResult());
    }

    public boolean isNotEmpty() {
        return (getResult().size() > 0);
    }

    
    public List getResult() {
        try {
            if (result == null) {
                runQuery();
            }
        } catch (RepositoryException e) {
            throw new RuntimeException(e);
        }

        return result;
    }

    public SortingInfo getSortingInfo() {
        try {
            if (sortingInfo == null) {
                runQuery();
            }
        } catch (RepositoryException e) {
            throw new RuntimeException(e);
        }

        return sortingInfo;
    }

    public ColumnInfo getColumnInfo() {
        try {
            if (columnInfo == null) {
                runQuery();
            }
        } catch (RepositoryException e) {
            throw new RuntimeException(e);
        }

        return columnInfo;
    }

    public void sortOrderChanged() {
        try {
            runQuery();
        } catch (RepositoryException e) {
            throw new RuntimeException(e);
        }
    }
    
    public String getQuery() {
        init();
        return query;
    }
    
    public void setQuery(String query) {
        this.query = query;
    }
    
    public SearchType getQueryType() {
        return type;
    }
    
    public void setQueryType(SearchType queryType) {
        this.type = queryType;
    }
    
    public boolean isNewQuery() {
        return result == null;
    }
    
    public boolean isEmpty() {
        return query == null || query.length() == 0;
    }

    public SearchModel getModel() {
        return RepositoryUtils.getSearchModel();
    }

    public void setResult(List result) {
        this.result = result;
    }

    public void setColumnInfo(ColumnInfo columnInfo) {
        this.columnInfo = columnInfo;
    }

    public void setSortingInfo(SortingInfo sortingInfo) {
        this.sortingInfo = sortingInfo;
    }
    
}
